import { _decorator, Animation, Component, director, instantiate, Label, Node, Prefab } from 'cc';
import { CHANGE_BOARD, CHECK_CLEAR, CHECK_COMPLETE, CHECK_LOSED } from './Event';
import { AUDIO_EFFECT_ENUM, GAME_EVENT_ENUM, GAME_SCENE_ENUM, GAME_STATE_ENUM, GMAE_BOARD_ENUM } from './Enum';
import { DataManager } from './DataManager';
import { levels } from './Level';
import { shuffle } from './Utils';
import { Block } from './Block';
const { ccclass, property } = _decorator;

@ccclass('GameManager')
export class GameManager extends Component {
    @property(Node)
    boardLevelNode: Node = null;
    @property(Node)
    boardLevelExtendNode: Node = null;
    @property(Node)
    boardRandomLeftNode: Node = null;
    @property(Node)
    boardRandomRightNode: Node = null;
    @property(Node)
    boardSlotNode: Node = null;
    @property(Node)
    boardHideNode: Node = null;
    @property(Node)
    gameOverNode: Node = null;
    @property(Node)
    gameCompleteNode: Node = null;

    @property(Prefab)
    blockPrefab: Prefab = null;
    @property(Label)
    titleLabel: Label = null;

    protected onLoad(): void {
        director.preloadScene(GAME_SCENE_ENUM.MENU) // 预加载菜单模块
        // 注册事件
        CHANGE_BOARD.on(GAME_EVENT_ENUM.CHANGE_BOARD, this.onChangeBoard, this) // 场景切换事件
        CHECK_CLEAR.on(GAME_EVENT_ENUM.CHECK_CLEAR, this.onCheckClear, this) // 清除block事件
        CHECK_COMPLETE.on(GAME_EVENT_ENUM.CHECK_COMPLETE, this.onCheckComplete, this) // 检查完成事件
        CHECK_LOSED.on(GAME_EVENT_ENUM.CHECK_LOSED, this.onCheckLosed, this) // 检查失败事件 

        // 开始游戏
        this.onGameStart()
    }

    // 开始游戏
    onGameStart() {
        // 加载数据
        DataManager.instance.restore()
        // 初始化数据
        this.initGame(DataManager.instance.level)
        // 修改游戏状态
        DataManager.instance.gameStatus = GAME_STATE_ENUM.RUNNNING
    }

    // 重置游戏
    onGameRestart() {

    }

    // 下一关
    onGameNext() {

    }


    //#region 游戏技能
    // 移除（放入扩展区）
    onGameExtend() {

    }
    // 撤销  
    onUndo() {

    }
    // 洗牌
    onAmeshuffle() {

    }
    // 明牌
    onClickable() {

    }
    //#endregion


    onChangeBoard(block: Block) {
        let board = this.boardLevelNode

        switch (block.boardType) {
            case GMAE_BOARD_ENUM.LEVEL:
                // 层级
                board = this.boardLevelNode
                break;
            case GMAE_BOARD_ENUM.LEVEL_EXTEND:
                // 层级扩展
                board = this.boardLevelExtendNode
                break;
            case GMAE_BOARD_ENUM.RANDOM_LEFT:
                // 随即左侧
                board = this.boardRandomLeftNode
                break;
            case GMAE_BOARD_ENUM.RANDOM_RIGHT:
                // 随即右侧
                board = this.boardRandomRightNode
                break;
            case GMAE_BOARD_ENUM.SLOT:
                // 插槽
                board = this.boardSlotNode
                break;
            case GMAE_BOARD_ENUM.HIDE:
                // 隐藏
                board = this.boardHideNode
                break;
            default:
                board = this.boardHideNode
        }
        // 挂载到对应的层级上
        block.node.setParent(board)
    }

    onCheckClear(block: Block) {
        // 获取槽中所有的block
        let slot_locks = DataManager.instance.blocks.filter(item => item.boardType === GMAE_BOARD_ENUM.SLOT)
        // 过滤出槽中所有与本次点击一致的卡片
        let slot_same = slot_locks.filter(item => item.type === block.type)

        if (slot_same.length >= DataManager.instance.currentLevel.clearableNum) {
            DataManager.instance.gameStatus = GAME_STATE_ENUM.CLEAR
            // 播放消除音乐
            CHECK_CLEAR.emit(GAME_EVENT_ENUM.PLAY_AUDIO, AUDIO_EFFECT_ENUM.CLEAR)

            slot_same.forEach(target => {
                // 删除操作
                if (DataManager.instance.records.findIndex(item => target.id === item.id) >= 0) {
                    DataManager.instance.records.splice(DataManager.instance.records.findIndex(item => target.id === item.id), 1)
                }

                // 获取动画组件 
                let anim = target.node.getComponent(Animation);
                // 播放动画事件注册
                anim.off(Animation.EventType.PLAY, this.onClearPlay, target);
                anim.on(Animation.EventType.PLAY, this.onClearPlay, target);
                // 播放动画事件注册
                anim.off(Animation.EventType.STOP, this.onClearStop, target);
                anim.on(Animation.EventType.STOP, this.onClearStop, target);

                //播放动画
                anim.play()
            });
        } else {
            if (slot_locks.length >= DataManager.instance.currentLevel.slotNum) {
                CHECK_LOSED.emit(GAME_EVENT_ENUM.CHECK_LOSED, this)
            }
        }
    }

    onCheckComplete() {
        // TODO: 检查完成
    }

    onCheckLosed() {
        // TODO: 检查失败 
    }

    // 返回菜单
    onBackMenu() {

    }

    // 动画开始
    onClearPlay() {
        this.node.getChildByName('bg').active = false
    }

    // 动画结束
    onClearStop() {
        this.node.getChildByName('bg').active = true
        // 设置Block状态为清除 
        this.node.getComponent(Block).boardType = GMAE_BOARD_ENUM.HIDE
        // 判断是不是所有的板块都隐藏了，如果是表示游戏获胜
        let unHideBlocks = DataManager.instance.blocks.filter(item => item.boardType != GMAE_BOARD_ENUM.HIDE)
        if (unHideBlocks.length <= 0) {
            CHECK_COMPLETE.emit(GAME_EVENT_ENUM.CHECK_COMPLETE, this)
        } else {
            DataManager.instance.gameStatus = GAME_STATE_ENUM.RUNNNING
        } 
    } 

    // 初始化棋盘
    initChessBox(tidth: number, height: number) {
        let chessBox = new Array()
        for (let i = 0; i < tidth; i++) {
            chessBox[i] = new Array()
            for (let j = 0; j < height; j++) {
                // 初始化棋盘
                chessBox[i][j] = { blocks: [] }
            }
        }

        return chessBox
    }

    // 初始化游戏 
    initGame(num: number) {
        let currentLevel = levels[num - 1] // 获取当前关卡数据
        // 安全校验，如果当前关卡不存在，那么重置为第一关卡
        if (!currentLevel) {
            if (levels[0]) {
                num = 1
                currentLevel = levels[0]
                DataManager.instance.level = num // 当前是第几关
            } else {
                return
            }
        }
        DataManager.instance.currentLevel = currentLevel // 当前关卡详细信息
        // 设置关卡标题
        this.titleLabel.string = `第${num}关`

        // 计算可以消除的总块数 （满足消除的块数 * 总共几种卡片）
        const blockUnit = currentLevel.clearableNum * currentLevel.BlockTypeNum
        // 计算每层方块数
        let totalBlockNum = currentLevel.leftRandomBlocks
            + currentLevel.rightRandomBlocks
            + currentLevel.levelNum * currentLevel.levelBlockNum
        // 调整方块数，让其可以被消除的总块数整除
        if (totalBlockNum % blockUnit !== 0) {
            totalBlockNum = (Math.floor(totalBlockNum / blockUnit) + 1) * blockUnit
        }

        // 当前关卡所有的card实例数组
        let cardArr: number[] = []
        // 获取本关卡需要用到的card
        const cards = currentLevel.blockTypeArr.slice(0, currentLevel.BlockTypeNum)
        // 填充方块数组
        for (let i = 0; i < totalBlockNum; i++) {
            // 随机获取card
            cardArr.push(cards[i % currentLevel.BlockTypeNum])
        }

        // 另一种方法获取
        // let set: Set<number> = new Set()
        // for (let i = 0; i < totalBlockNum; i+currentLevel.BlockTypeNum) {
        //     // 随即获取card
        //     let item = Math.floor(Math.random() * currentLevel.BlockTypeNum)
        //     // 每次获取三个，使用Set保存，无序
        //     let j: number = 0
        //     while (j < currentLevel.BlockTypeNum) {
        //         set.add(item)
        //         j+=1
        //     }
        // }  

        // 打乱数组顺序
        cardArr = shuffle(cardArr)

        // 所有的block半初始化数组
        let blockArr: BlockType[] = []

        for (let i = 0; i < totalBlockNum; i++) {
            blockArr.push({
                id: i,
                x: null,
                y: null,
                width: currentLevel.chessItemWidth * 3,
                height: currentLevel.chessItemHeight * 3,
                level: 0,
                boardType: null,
                type: cardArr[i],
                hignerIds: [], // 当前哪些块高于block的id集合
                lowerIds: [] // 当前哪些块低于block的id集合
            } as BlockType)
        }

        let pos = 0
        // 初始化随即区域中的块
        // 生成左侧的随机块
        // 生成右侧的随机块

        /**
         * 什么时候生成排列规律的块
         * 1. leftRandomBlocks == 0
         * 2. rightRandomBlocks == 0
         * 3. levelBlockNum % clickableNum == 0
         * 4. levelNum * levelBlockNum % clickableNum * blockTypeNum == 0
         * 5. levelBlockNum <= 16
         */
        /**
         * 1. 检查左随机区域方块数为0
         * 2. 检查右随机区域方块数为0
         * 3. 关卡的总方块数可以被可消除方块数整除
         * 4. 关卡的总方块数可以被可消除方块数和方块种类数整除
         * 5. 关卡层方块数小于等于16
         */

        let isRandom = true
        if (currentLevel.leftRandomBlocks == 0
            && currentLevel.rightRandomBlocks == 0
            && (currentLevel.levelBlockNum % currentLevel.clearableNum == 0)
            && ((currentLevel.levelNum * currentLevel.levelBlockNum) % (currentLevel.clearableNum * currentLevel.BlockTypeNum) == 0)
            && (currentLevel.levelBlockNum <= 16)
        ) {
            isRandom = false
        }

        // 初始化棋盘
        let chessBox = this.initChessBox(currentLevel.chessWidthNum, currentLevel.chessHeightNum)

        // 保存所有完全初始化后的block，包含位置等任何信息
        let chessBlocks: BlockType[] = []

        // 初始化关卡中的块
        let remainBlockNum = totalBlockNum - currentLevel.leftRandomBlocks - currentLevel.rightRandomBlocks
        // 利用剩下的块按层分配
        let minWidth = 0,
            maxWidth = currentLevel.chessWidthNum - 2,
            minHeight = 0,
            maxHeight = currentLevel.chessHeightNum - 2;

        for (let i = 0; i < currentLevel.levelNum; i++) {
            let blockNum = Math.min(currentLevel.levelBlockNum, totalBlockNum)
            // 最后一层特殊处理
            if (i == currentLevel.levelNum - 1) {
                blockNum = remainBlockNum
            }

            // 便捷收缩逻辑
            if (currentLevel.blockBorderStep > 0 && i > 0) {
                switch (i % 4) {
                    case 0:
                        minWidth += currentLevel.blockBorderStep; // 向左收缩
                        // maxWidth -= currentLevel.blockBorderStep;
                        break;
                    case 1:
                        // minHeight += currentLevel.blockBorderStep;
                        maxHeight -= currentLevel.blockBorderStep; // 向上收缩
                        break;
                    case 2:
                        // minWidth -= currentLevel.blockBorderStep;
                        maxWidth += currentLevel.blockBorderStep; // 向右收缩
                        break;
                    case 3:
                        minHeight -= currentLevel.blockBorderStep; // 向下收缩
                        // maxHeight += currentLevel.blockBorderStep; 
                        break;
                }
            }

            // 获取本层需要用到的blocks
            const blocks = blockArr.slice(pos, pos + blockNum)
            pos += blockNum // 下次循环开始的位置，避免重复或者遗漏掉某些block

            // 保存所有随机生成为位置的映射关系，避免重复生成相同位置上的块
            let blockPosSet = new Set<string>()

            for (let j = 0; j < blockNum; j++) {
                let block = blocks[j]
                let nx: number, ny: number, key: string
                if (isRandom) {
                    // 不满足规律的话，随机生成块的位置
                    nx = Math.floor(Math.random() * (maxWidth - minWidth + 1)) + minWidth
                    ny = Math.floor(Math.random() * (maxHeight - minHeight + 1)) + minHeight
                    key = `${nx}-${ny}`
                    while (blockPosSet.has(key)) {
                        nx = Math.floor(Math.random() * (maxWidth - minWidth + 1)) + minWidth
                        ny = Math.floor(Math.random() * (maxHeight - minHeight + 1)) + minHeight
                        key = `${nx}-${ny}`
                    }
                } else {
                    // 满足规律的情况，审成有规律的块
                    // 计算当前层级的block数的平方根并向下取整
                    let sqrt = Math.floor(Math.sqrt(currentLevel.levelBlockNum))
                    // 根据当前棋盘的宽度和当前层级block数的平方根计算nx的值
                    // j % sqrt * 4 ： 计算当前层级的块在棋盘上的x坐标位置
                    // Math.floor(currentLevel.chessItemWidth - 3 * sqrt) / 2 ：计算偏移量，用于块组居中
                    nx = j % sqrt * 4 + Math.floor((currentLevel.chessItemWidth - 3 * sqrt) / 2)
                    // 如果sqrt时偶数，则nx向左移动一个单位
                    if (sqrt % 2 == 0) nx -= 1
                    // 根据当前棋盘的宽度和当前层级block数的平方根计算ny的值
                    ny = Math.floor(j / sqrt) * 5 + i
                    key = `${nx}-${ny}`
                }
                chessBox?.[nx]?.[ny]?.blocks?.push(block)
                blockPosSet.add(key)

                // 计算堆叠情况
                let minX = Math.max(nx - 2, 0)
                let minY = Math.max(ny - 2, 0)
                let maxX = Math.min(nx + 2, maxWidth)
                let maxY = Math.min(ny + 2, maxHeight)

                let maxLevel = 0
                for (let x = minX; x <= maxX; x++) {
                    for (let y = minY; y <= maxY; y++) {
                        let nearlayBlocks = chessBox?.[x]?.[y]?.blocks
                        if (nearlayBlocks?.length > 0) {
                            // 去除当前块下方的最上面的块
                            let topestBlock = nearlayBlocks[nearlayBlocks?.length - 1]

                            if (topestBlock.id === block.id) continue

                            // 更新当前层级最高的块
                            maxLevel = Math.max(maxLevel, topestBlock.level)

                            // 记录当前块比topestBlock高
                            topestBlock.hignerIds.push(block.id)
                            // 记录当前块比topestBlock矮 
                            block.lowerIds.push(topestBlock.id)
                        }
                    }
                }
                // 填充剩余的属性，如位置
                block.boardType = GMAE_BOARD_ENUM.LEVEL // 刚生成的块都是在每个层级中
                block.x = nx * currentLevel.chessItemWidth // 这里乘以物理大小，所以x表示实际的位置，单位px
                block.y = ny * currentLevel.chessItemHeight // 这里乘以物理大小，所以x表示实际的位置，单位px
                block.level = maxLevel + 1 // 当前块实际层级
            }

            // 将当前批次解构并且放入chessBlocks中，便于后续处理
            chessBlocks.push(...blocks)

            remainBlockNum -= blockNum
            // 如果剩余的不够，则跳出循环
            if (remainBlockNum <= 0) break
        }

        DataManager.instance.blocks

        // 棋盘渲染
        chessBlocks.forEach(block => {
            let node = instantiate(this.blockPrefab) // 获取Block预制体
            node.getComponent(Block).init(block)
        })

        console.log(DataManager.instance.blocks)

        // 棋盘区域 居中调整
        let x = currentLevel.chessWidthNum * currentLevel.chessItemWidth / 2 * -1 + currentLevel.chessWidthNum
        let y = currentLevel.chessHeightNum * currentLevel.chessItemHeight / 2 - 300
        this.boardLevelNode.setPosition(x, y)
    }

    protected onDestroy(): void {
        CHANGE_BOARD.off(GAME_EVENT_ENUM.CHANGE_BOARD, this.onChangeBoard, this)
        CHECK_CLEAR.off(GAME_EVENT_ENUM.CHECK_CLEAR, this.onCheckClear, this)
        CHECK_COMPLETE.off(GAME_EVENT_ENUM.CHECK_COMPLETE, this.onCheckComplete, this)
        CHECK_LOSED.off(GAME_EVENT_ENUM.CHECK_LOSED, this.onCheckLosed, this)
    }

}

